package program.controller;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import program.domain.Memory;
import program.domain.PCB;
import program.services.BestFit;
import program.services.FirstFit;
import program.services.WorstFit;
import program.utils.FileUtil;
import program.utils.PCBQueue;
import program.utils.ViewUtil;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.ResourceBundle;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @program: OSAlgorithms
 * @description: 内存分配控制器
 * @author: Echo-dev
 * @create: 2021-11-10 17:13
 **/
public class MemoryController implements Initializable {

    public static MemoryController memoryController;

    private PCBQueue readyQueue = new PCBQueue();

    private PCBQueue finishQueue = new PCBQueue();

    private ArrayList<Memory> memories;

    private FirstFit firstFit = new FirstFit();

    private BestFit bestFit = new BestFit();

    private WorstFit worstFit = new WorstFit();

    private int maxHeight;

    private int time;

    @FXML
    private VBox mainBox;

    @FXML
    private VBox leftBox;

    @FXML
    private Text timeText;

    @FXML
    private TableView<Memory> freeTable;

    @FXML
    private TableColumn<Memory, Integer> startCol;

    @FXML
    private TableColumn<Memory, Integer> lengthCol;

    public void FirstFitAllocate() {
        initView();
        firstFit.setFree(memories);
        firstFit.init();
        loadTable(memories);
        initReadyQueue();
        firstFit.setReady(readyQueue);

        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(3);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                timeText.setText(String.valueOf(time));
                firstFit.allocate();
                memories = firstFit.getFree();

                loadTable(firstFit.getFree());
                loadView(firstFit.getFree());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                firstFit.checkFinish();

                if (readyQueue.getSize() == 0) {
                    time++;
                    ArrayList<Memory> running = firstFit.getUsed();
                    for (Memory memory : running) {
                        PCB pcb = memory.getPcb();
                        if (pcb != null) {
                            pcb.setNeedTime(pcb.getNeedTime() - 1);
                        }
                    }
                }
                System.out.println("当前空闲区：");
                for (Memory memory : memories) {
                    System.out.println("起始地址:" + memory.getStart() + "  大小：" + memory.getLength());
                }
                System.out.println();

                loadView(firstFit.getFree());
                loadTable(firstFit.getFree());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        };
        scheduledThreadPoolExecutor.scheduleAtFixedRate(runnable, 2, 2, TimeUnit.SECONDS);

    }

    public void BestFitAllocate() {
        initView();
        bestFit.setFree(memories);
        bestFit.init();
        loadTable(memories);
        initReadyQueue();
        bestFit.setReady(readyQueue);

        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(3);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                timeText.setText(String.valueOf(time));
                loadTable(bestFit.getFree());
                bestFit.allocate();
                memories = bestFit.getFree();
                loadView(bestFit.getFree());
                loadTable(bestFit.getFree());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                bestFit.checkFinish();

                if (readyQueue.getSize() == 0) {
                    time++;
                    ArrayList<Memory> running = bestFit.getUsed();
                    for (Memory memory : running) {
                        PCB pcb = memory.getPcb();
                        if (pcb != null) {
                            pcb.setNeedTime(pcb.getNeedTime() - 1);
                        }
                    }
                }

                System.out.println("当前空闲区：");
                for (Memory memory : memories) {
                    System.out.println("起始地址:" + memory.getStart() + "  大小：" + memory.getLength());
                }
                System.out.println();

                loadView(bestFit.getFree());
                loadTable(bestFit.getFree());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        scheduledThreadPoolExecutor.scheduleAtFixedRate(runnable, 2, 2, TimeUnit.SECONDS);

    }

    public void WorstFitAllocate() {
        initView();
        worstFit.setFree(memories);
        worstFit.init();
        loadTable(memories);
        initReadyQueue();
        worstFit.setReady(readyQueue);

        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(3);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                timeText.setText(String.valueOf(time));
                worstFit.allocate();
                memories = worstFit.getFree();
                loadView(worstFit.getFree());
                loadTable(bestFit.getFree());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                worstFit.checkFinish();

                if (readyQueue.getSize() == 0) {
                    time++;
                    ArrayList<Memory> running = worstFit.getUsed();
                    for (Memory memory : running) {
                        PCB pcb = memory.getPcb();
                        if (pcb != null) {
                            pcb.setNeedTime(pcb.getNeedTime() - 1);
                        }
                    }
                }

                System.out.println();
                loadTable(worstFit.getFree());
                loadView(worstFit.getFree());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        scheduledThreadPoolExecutor.scheduleAtFixedRate(runnable, 2, 2, TimeUnit.SECONDS);

    }

    private void initReadyQueue() {
        readyQueue.add(new PCB("进程1", 3, 6));
        readyQueue.add(new PCB("进程2", 5, 9));
        readyQueue.add(new PCB("进程3", 1, 2));
        readyQueue.add(new PCB("进程4", 2, 7));
        readyQueue.add(new PCB("进程5", 6, 3));
    }

    private void initView() {
        String fileName = "memory.txt";
        memories = FileUtil.getMemoryData(fileName);
        //获取最大的地址值
        maxHeight = memories.get(memories.size() - 1).getStart() + memories.get(memories.size() - 1).getLength();

        for (Memory memory : memories) {
            //添加地址文本
            Label label = ViewUtil.createAdrLabel(memory, maxHeight);
            leftBox.getChildren().add(label);

            //添加内存图形
            Rectangle rectangle = ViewUtil.createRectangle(memory, maxHeight);
            mainBox.getChildren().add(rectangle);
        }
    }

    private void loadView(ArrayList<Memory> free) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {

                leftBox.getChildren().clear();
                mainBox.getChildren().clear();

                ViewUtil.setHeightSum(0);
                ViewUtil.setHeightSumL(0);

                memories = free;
                for (Memory memory : memories) {
                    //添加地址文本
                    Label label = ViewUtil.createAdrLabel(memory, maxHeight);
                    leftBox.getChildren().add(label);

                    //添加内存图形
                    Rectangle rectangle = ViewUtil.createRectangle(memory, maxHeight);
                    mainBox.getChildren().add(rectangle);
                }
            }
        });

    }

    private void loadTable(ArrayList<Memory> frees) {
        ObservableList<Memory> free = FXCollections.observableArrayList();
        Collections.sort(frees, new Comparator<Memory>() {
            @Override
            public int compare(Memory o1, Memory o2) {
                return o1.getStart() - o2.getStart();
            }
        });

        for (Memory memory : frees) {
            free.add(memory);
        }

        startCol.setCellValueFactory(new PropertyValueFactory<Memory, Integer>("start"));
        lengthCol.setCellValueFactory(new PropertyValueFactory<Memory, Integer>("length"));
        freeTable.refresh();
        freeTable.setItems(free);

    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        memoryController = this;
    }

}
